package cn.ococci.dm.WiFi;

import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.AuthAlgorithm;
import android.net.wifi.WifiConfiguration.GroupCipher;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.PairwiseCipher;
import android.net.wifi.WifiConfiguration.Protocol;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.SystemClock;
import android.text.TextUtils;

import java.util.Comparator;
import java.util.List;

import cn.ococci.dm.Main.Const;
import cn.ococci.dm.Main.Tools.LogTool;
import cn.ococci.dm.Main.Tools.StringTool;


public class WiFi {
	

	private static int index = -1;


	// Constants used for different security types
	public static final String WPA2 = "WPA2";
	public static final String WPA = "WPA";
	public static final String WEP = "WEP";
	public static final String OPEN = "Open";
	// For EAP Enterprise fields
	public static final String WPA_EAP = "WPA-EAP";
	public static final String IEEE8021X = "IEEE8021X";

	public static final String[] EAP_METHOD = { "PEAP", "TLS", "TTLS" };

	public static final int WEP_PASSWORD_AUTO = 0;
	public static final int WEP_PASSWORD_ASCII = 1;
	public static final int WEP_PASSWORD_HEX = 2;

	private static final String TAG = "Wifi Connecter";


	/**
	 * Configure a network, and connect to it.
	 * @param wifiMgr
	 * @param scanResult
	 * @param password Password for secure network or is ignored.
	 * @return
	 */
	public static boolean connectToNewNetwork(final WifiManager wifiMgr, final ScanResult scanResult, final String password,ConnectivityManager  cm,CheckNetWorkCallback callback) {
		final String security = getScanResultSecurity(scanResult);  //获取当前需要连接WiFi的安全类型
		if(security.equals(Const.Wifi.OPEN)) {
			final int numOpenNetworksKept = 30;
			checkForExcessOpenNetworkAndSave(wifiMgr, numOpenNetworksKept);
		}

		//obtain Configuration
		WifiConfiguration config = new WifiConfiguration();
		config.SSID = StringTool.convertToQuotedString(scanResult.SSID);
		config.BSSID = scanResult.BSSID;


		//根据security 来配置一些连接信息
		setupSecurity(config, security, password);
		int id = wifiMgr.addNetwork(config);
		LogTool.log(LogTool.CYAN,"连接的网络ID：" + id);
		if(id == -1 || !wifiMgr.saveConfiguration()) {
			return false;
		}
		config = getWifiConfiguration(wifiMgr, config, security);

		if(config == null) {
			return false;
		}
		LogTool.log(LogTool.CYAN,"开始连接");
		return connectToConfiguredNetwork(wifiMgr, config, true,cm,callback);
	}
	
	/**
	 * Connect to a configured network.
	 * @param wifiMgr
	 * @param config
	 * @param  reassociate
	 * @return
	 */
	public static boolean  connectToConfiguredNetwork(final WifiManager wifiMgr, WifiConfiguration config, boolean reassociate,ConnectivityManager cm,CheckNetWorkCallback callback) {
		final String security = getWifiConfigurationSecurity(config);
		
		int oldPri = config.priority;
		int newPri = getMaxPriority(wifiMgr) + 1;
		if(newPri > MAX_PRIORITY) {
			newPri = shiftPriorityAndSave(wifiMgr);
			config = getWifiConfiguration(wifiMgr, config, security);
			if(config == null) {
				return false;
			}
		}
		
		// Set highest priority to this configured network
		config.priority = newPri;
		int networkId = wifiMgr.updateNetwork(config);
		if(networkId == -1) {
			return false;
		}
		
		// Do not disable others
		if(!wifiMgr.enableNetwork(networkId, false)) {
			config.priority = oldPri;
			return false;
		}
		
		if(!wifiMgr.saveConfiguration()) {
			config.priority = oldPri;
			return false;
		}

		config = getWifiConfiguration(wifiMgr, config, security);
		if(config == null) {
			return false;
		}

		boolean readyConnecting = wifiMgr.enableNetwork(config.networkId, true);
		if(!readyConnecting){
			return false;
		}
		LogTool.log(LogTool.CYAN,"开始连接..");

		while(wifiMgr.enableNetwork(config.networkId, true)){
			NetworkInfo mWiFiNetworkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
			WifiInfo info =  wifiMgr.getConnectionInfo();

			if(mWiFiNetworkInfo != null && config.SSID.equals(info.getSSID()) && mWiFiNetworkInfo.isAvailable() && mWiFiNetworkInfo.isConnected()){
				callback.onCheckSuccess();
				break;
			}
			SystemClock.sleep(100);

		}
		LogTool.log(LogTool.CYAN,"开始连接...1");

		if(!wifiMgr.enableNetwork(config.networkId, true)) {
			return false;
		}
		
		final boolean connect = reassociate ? wifiMgr.reassociate() : wifiMgr.reconnect();
		if(!connect) {
			return false;
		}
		
		return true;
	}

	/**
	 * 根据Priority排序
	 * @param configurations
	 */
	private static void sortByPriority(final List<WifiConfiguration> configurations) {

		if(StringTool.hasData(configurations)){

			java.util.Collections.sort(configurations, new Comparator<WifiConfiguration>() {

				@Override
				public int compare(WifiConfiguration object1,WifiConfiguration object2) {
					return object1.priority - object2.priority;
				}
			});
		}else {

			LogTool.log(LogTool.CYAN," fuck configurations null");

		}


	}

	/**
	 * Ensure no more than numOpenNetworksKept open networks in configuration list.
	 * @param wifiMgr
	 * @param numOpenNetworksKept
	 * @return Operation succeed or not.
	 */
	private static boolean checkForExcessOpenNetworkAndSave(final WifiManager wifiMgr, final int numOpenNetworksKept) {
		final List<WifiConfiguration> configurations = wifiMgr.getConfiguredNetworks();
		sortByPriority(configurations);

		boolean modified = false;
		int tempCount = 0;
		for(int i = configurations.size() - 1; i >= 0; i--) {
			final WifiConfiguration config = configurations.get(i);
			if(getWifiConfigurationSecurity(config).equals(OPEN)) {
				tempCount++;
				if(tempCount >= numOpenNetworksKept) {
					modified = true;
					wifiMgr.removeNetwork(config.networkId);
				}
			}
		}
		if(modified) {
			return wifiMgr.saveConfiguration();
		}

		return true;
	}
	
	private static final int MAX_PRIORITY = 99999;
	
	private static int shiftPriorityAndSave(final WifiManager wifiMgr) {

		final List<WifiConfiguration> configurations = wifiMgr.getConfiguredNetworks();
		if(!StringTool.hasData(configurations)) {
			return -1;
		}
		sortByPriority(configurations);
		final int size = configurations.size();
		for(int i = 0; i < size; i++) {
			final WifiConfiguration config = configurations.get(i);
			config.priority = i;
			wifiMgr.updateNetwork(config);
		}
		wifiMgr.saveConfiguration();
		return size;
	}

	private static int getMaxPriority(final WifiManager wifiManager) {
		final List<WifiConfiguration> configurations = wifiManager.getConfiguredNetworks();
		int pri = 0;
		for(final WifiConfiguration config : configurations) {
			if(config.priority > pri) {
				pri = config.priority;
			}
		}
		return pri;
	}


	public static WifiConfiguration getWifiConfiguration(final WifiManager wifiMgr, final WifiConfiguration configToFind, String security) {
		index =  - 1;
		final String ssid = configToFind.SSID;
		if(ssid.length() == 0) {
			return null;
		}
		final String bssid = configToFind.BSSID;

		if(security == null) {
			security = getWifiConfigurationSecurity(configToFind);
		}
		
		final List<WifiConfiguration> configurations = wifiMgr.getConfiguredNetworks();

		for(int i = 0;i < configurations.size() ;i++){
			WifiConfiguration config = configurations.get(i);
			if(config.SSID == null || !ssid.equals(config.SSID)) {
				continue;
			}
			if(config.BSSID == null || bssid == null || bssid.equals(config.BSSID)) {
				final String configSecurity = getWifiConfigurationSecurity(config);
				if(security.equals(configSecurity)) {
					index = i;
					return config;
				}
			}
		}
		return null;
	}


	/**
	 * return give 获取Wifi安全类型
	 * @param wifiConfig
	 * @return
	 */
    static public String getWifiConfigurationSecurity(WifiConfiguration wifiConfig) {
		String security = Const.Wifi.OPEN;
        if (wifiConfig.allowedKeyManagement.get(KeyMgmt.NONE)) {
            if (!wifiConfig.allowedGroupCiphers.get(GroupCipher.CCMP)
                    && (wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP40)
                            || wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP104))) {
				security = Const.Wifi.WEP;
            } else {
				security =  Const.Wifi.OPEN;
            }
        } else if (wifiConfig.allowedProtocols.get(Protocol.RSN)) {
			security =  Const.Wifi.WPA2;
        } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
			security =  Const.Wifi.WPA_EAP;
        } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
			security =   Const.Wifi.IEEE8021X;
        } else if (wifiConfig.allowedProtocols.get(Protocol.WPA)) {
			security =  Const.Wifi.WPA;
        }

		return security;

    }

	/**
	 * Fill in the security fields of WifiConfiguration config.
	 * @param config The object to fill.
	 * @param security If is OPEN, password is ignored.
	 * @param password Password of the network if security is not OPEN.
	 */
	static private void setupSecurity(WifiConfiguration config, String security, final String password) {
		config.allowedAuthAlgorithms.clear();
		config.allowedGroupCiphers.clear();
		config.allowedKeyManagement.clear();
		config.allowedPairwiseCiphers.clear();
		config.allowedProtocols.clear();

		if (TextUtils.isEmpty(security)) {
			security = OPEN;
		}

		if (security.equals(WEP)) {
			int wepPasswordType = WEP_PASSWORD_AUTO;
			if (!TextUtils.isEmpty(password)) {
				if (wepPasswordType == WEP_PASSWORD_AUTO) {
					if (isHexWepKey(password)) {
						config.wepKeys[0] = password;
					} else {
						config.wepKeys[0] = StringTool.convertToQuotedString(password);
					}
				} else {
					config.wepKeys[0] = wepPasswordType == WEP_PASSWORD_ASCII
							? StringTool.convertToQuotedString(password)
							: password;
				}
			}

			config.wepTxKeyIndex = 0;

			config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
			config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);

			config.allowedKeyManagement.set(KeyMgmt.NONE);

			config.allowedGroupCiphers.set(GroupCipher.WEP40);
			config.allowedGroupCiphers.set(GroupCipher.WEP104);

		} else if (security.equals(WPA) || security.equals(WPA2)){
			config.allowedGroupCiphers.set(GroupCipher.TKIP);
			config.allowedGroupCiphers.set(GroupCipher.CCMP);

			config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);

			config.allowedPairwiseCiphers.set(PairwiseCipher.CCMP);
			config.allowedPairwiseCiphers.set(PairwiseCipher.TKIP);

			config.allowedProtocols.set(security.equals(WPA2) ? Protocol.RSN : Protocol.WPA);

			// If password is empty, it should be left untouched
			if (!TextUtils.isEmpty(password)) {
				if (password.length() == 64 && isHex(password)) {
					// Goes unquoted as hex
					config.preSharedKey = password;
				} else {
					// Goes quoted as ASCII
					config.preSharedKey = StringTool.convertToQuotedString(password);
				}
			}

		} else if (security.equals(OPEN)) {
			LogTool.log(LogTool.CYAN,"安全类型");
			config.allowedKeyManagement.set(KeyMgmt.NONE);

		} else if (security.equals(WPA_EAP) || security.equals(IEEE8021X)) {
			config.allowedGroupCiphers.set(GroupCipher.TKIP);
			config.allowedGroupCiphers.set(GroupCipher.CCMP);
			if (security.equals(WPA_EAP)) {
				config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
			} else {
				config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
			}
			if (!TextUtils.isEmpty(password)) {
				config.preSharedKey = StringTool.convertToQuotedString(password);
			}
		}
	}
	
	private static boolean isHexWepKey(String wepKey) {
        final int len = wepKey.length();
        if (len != 10 && len != 26 && len != 58) {
            return false;
        }
        
        return isHex(wepKey);
    }
    
    private static boolean isHex(String key) {
        for (int i = key.length() - 1; i >= 0; i--) {
            final char c = key.charAt(i);
            if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) {
                return false;
            }
        }
        
        return true;
    }

    static final String[] SECURITY_MODES = { Const.Wifi.WEP, Const.Wifi.WPA, Const.Wifi.WPA2,  Const.Wifi.WPA_EAP,  Const.Wifi.IEEE8021X };
	
	/**
     * @return The security of a given {@link ScanResult}.
     */
    public static String getScanResultSecurity(ScanResult scanResult) {
        final String cap = scanResult.capabilities;
        for (int i = SECURITY_MODES.length - 1; i >= 0; i--) {
            if (cap.contains(SECURITY_MODES[i])) {
                return SECURITY_MODES[i];
            }
        }
        
        return Const.Wifi.OPEN;
    }


	/**
	 * Created by cyan on 15/10/13.
	 * 检测网络成功
	 */
	public interface CheckNetWorkCallback {

		/**
		 * 检测网络成功
		 */
		void  onCheckSuccess();

	}


}